﻿namespace MathVisualizer

module Parser =
    open Utils

    let rec parseE(tokens:string[], next:int byref) : Expr = 
        //let tokens = str.Trim().ToLower().Split([| '('; ','; ')' |])
        //let mutable next = _next
    
        let mutable currentexpr = Expr(Empty)//parseE(tokens, &next)
        
        let mutable stop = false
        //let isroot = (next=0)
        let tokenstart = next;
        let mutable debugi= 0
        while next < tokens.Length && not stop && (debugi<10) do
            debugi <- debugi + 1
            let mval = tokens.[next].Trim().ToLower()
            let hasnext = next < tokens.Length - 1
            let mutable islost = true
            let mutable ignore = false
            let tmp = match mval with
                            |"sum" | "sub" | "ratio" | "exponent" | "subscript" | "sqrt" | "concat" | "limit" | "larrow" | "rarrow" 
                            |"major" | "minor" | "geq" | "leq" | "approx" | "mul" when hasnext && tokens.[next + 1] = "(" -> 
                                //debug("parseE: sum/sub")
                                
                                     
                                next <- next + 2
                                let child1 = parseE(tokens, &next)
                                next <- next + 1
                                let child2 = parseE(tokens, &next)
                                let sumExpr = match mval with
                                                |"sum" -> Expr(Sum(child1, child2))
                                                |"sub" -> Expr(Sub(child1, child2))
                                                |"ratio" -> Expr(Ratio(child1, child2))
                                                |"exponent" -> Expr(Exponent(child1, child2))
                                                |"subscript" -> Expr(Subscript(child1, child2))
                                                |"sqrt" -> Expr(Sqrt(child1, child2))
                                                |"concat" -> Expr(Concat(child1, child2))
                                                |"limit" -> Expr(Limit(child1, child2))
                                                |"larrow" -> Expr(LeftArrow(child1, child2))
                                                |"rarrow" -> Expr(RightArrow(child1, child2))
                                                |"major" -> Expr(Major(child1, child2))
                                                |"minor" -> Expr(Minor(child1, child2))
                                                |"geq" -> Expr(Geq(child1, child2))
                                                |"leq" -> Expr(Leq(child1, child2))
                                                |"approx" -> Expr(Approx(child1, child2))
                                                |"mul" -> Expr(Mul(child1, child2))
                                                |_ -> Expr(Empty)
                                                
                                let closetoken = tokens.[next]
                                next <- next + 1
                                match closetoken with
                                    | ")" -> sumExpr
                                    | _ -> Expr(Empty)
                                
                                
                                
                            
                            | "matrix" when (tokens.Length - next) > 2 ->
                                //debug("PARSER: MATRIX")
                                
                                next <- next + 2
                                
                                let rowexpr = parseE(tokens, &next)
                                next <- next + 1
                                let colexpr = parseE(tokens, &next)

                                let mutable row=0
                                let mutable col=0

                                match rowexpr.Expr, colexpr.Expr with
                                    |Literal(rowstr), Literal(colstr) when System.Int32.TryParse(rowstr, &row) && System.Int32.TryParse(colstr, &col) ->
                                        printf "rowint: %i; colint: %i\n" row col 
                                        //let mutable row, col = System.Int32.Parse(rowexpr.ExprString), System.Int32.Parse(colexpr.ExprString)

                                
      
                                        //debug("PARSER: MATRIX: ROW: " + row.ToString() + "; COL: " + col.ToString())
                                        next <- next + 1
                                        let mutable exprs = [| |]
                                        while (next < tokens.Length) && (tokens.[next] <> ")") do
                                            next <- next + 1
                                            //debug("PARSER: MATRIX: NEXT EXPR: " + tokens.[next])
                                            exprs <- Array.append exprs [| parseE(tokens, &next) |]
                                        next <- next + 1        
                                        //debug("PARSER: MATRIX: row*col = " + (row*col).ToString() + "; exprs.Length = " + exprs.Length.ToString())        
                               
                                        let result = if row*col = exprs.Length then 
                                                        Expr(Matrix(row, col, exprs))
                                                        else Expr(Empty)

                                        result
                                    | _, _ -> Expr(Empty)
                            | "bigsum" | "int" | "union" | "intersect" | "productor" ->
                                next <- next + 2
                                let child1 = parseE(tokens, &next)
                                next <- next + 1
                                let child2 = parseE(tokens, &next)
                                next <- next + 1
                                let resexpr = match mval with
                                                | "bigsum" -> Expr(BigSum(child1, child2, parseE(tokens, &next)))
                                                | "int" -> Expr(Integral(child1, child2, parseE(tokens, &next)))
                                                | "union" -> Expr(Union(child1, child2, parseE(tokens, &next)))
                                                | "intersect" -> Expr(Intersection(child1, child2, parseE(tokens, &next)))
                                                | "productor" -> Expr(Productor(child1, child2, parseE(tokens, &next)))
                                                | _ -> Expr(Empty)

                                next <- next + 1
                                resexpr
                            | "+" | "-" | "/" | "=" | "^" | "_" | "*" | ">" | "<"  ->
                                //debug("sum contract")
                                islost <- false
                                next <- next + 1
                                let secondchild = parseE(tokens, &next)
                                //debug("sum contract returning")
                                
                                let res =match mval with
                                            |"+" -> Expr(Sum(currentexpr, secondchild))
                                            |"-" -> Expr(Sub(currentexpr, secondchild))
                                            |"/" -> Expr(Ratio(currentexpr, secondchild))
                                            |"=" -> Expr(Equals(currentexpr, secondchild))
                                            |"^" -> Expr(Exponent(currentexpr, secondchild))
                                            |"_" -> Expr(Subscript(currentexpr, secondchild))
                                            |"*" -> Expr(Mul(currentexpr, secondchild))
                                            |">" -> Expr(Major(currentexpr, secondchild))
                                            |"<" -> Expr(Minor(currentexpr, secondchild))
                                           
                                            | _ -> Expr(Empty)  

                                res.IsContract <- true
                                res
                            
                            |"(" | "[" | "{" ->
                                next <- next + 1
                                debug("opened " + mval)
                                let resultExpr = match mval with 
                                                    | "(" -> Expr(Round(parseE(tokens, &next)))
                                                    | "[" -> Expr(Square(parseE(tokens, &next)))
                                                    | "{" -> Expr(Group(parseE(tokens, &next)))
                                                    | _ -> Expr(Empty)
                                
                                match tokens.[next] with
                                    | ")" when mval="(" -> debug("closed )"); next <- next + 1; resultExpr
                                    | "]" when mval="[" -> debug("closed ]"); next <- next + 1; resultExpr
                                    | "}" when mval="{" -> debug("closed }"); next <- next + 1; stop<-true; resultExpr
                                    | _ -> raise (InvalidExpression("no match " + mval + "; founded: " + tokens.[next])) ; Expr(Literal("no match " + mval + " (founded: " + tokens.[next] + ")")) 
                            | "," -> debug("found: ,; stopping"); stop <- true; islost <- false; currentexpr 
                            | ")" | "]" | "}" -> stop <- true; islost<-false; currentexpr
                            | "\\" ->
                                //next <- next + 1
                                let escaped = tokens.[next+1]
                                let isgreek, greekletter = isGreekLetter (escaped)
                                printf "ESCAPING %s\n" escaped
                                let resultExpr = match isgreek, escaped with
                                                    |false, "{" -> 
                                                        next <- next + 2
                                                        printf "Opened escaped {\n" 
                                                        let expr = Expr(Brace(parseE(tokens, &next)))
                                                        match tokens.[next], tokens.[next+1] with
                                                            |"\\", "}" -> printf "Closed Escaped }; first token is:%s\n" tokens.[next]; next <- next+2; expr
                                                            | _, _ -> printf "Escaped {. Clousure not found! first token is:%s\n" tokens.[next];  next <- next+1; Expr(Empty)
                                                       
                                                    |false, "}" -> stop<-true; islost<-false; currentexpr
                                                    |false, "to" |false, "from" | false, "geq" | false, "leq" | false, "appr" ->
                                                        islost <- false
                                                        next <- next + 2
                                                        let res = match escaped with
                                                                    |"to" -> Expr(RightArrow(currentexpr, parseE(tokens, &next)))
                                                                    |"from" -> Expr(LeftArrow(currentexpr, parseE(tokens, &next)))
                                                                    |"geq" -> Expr(Geq(currentexpr, parseE(tokens, &next)))
                                                                    |"leq" -> Expr(Leq(currentexpr, parseE(tokens, &next)))
                                                                    |"appr" -> Expr(Approx(currentexpr, parseE(tokens, &next)))
                                                                    | _ -> Expr(Empty)
                                                        res.IsContract <- true
                                                        res
                                                    
                                                    | _, _ -> next <- next + 2; Expr(Literal(greekletter))
                                
                                
                                resultExpr
                            | " " | "" -> printf "FOUND BLANCK CHAR. IGNORE.\n"; ignore <- true; next <- next+1; currentexpr
                            | _ -> 
                                //debug("parseE: noMatch: " + tokens.[next].ToString())
                                let cur = tokens.[next]
                                
                                (*let res= match cur, currentexpr.Expr with 
                                            | "", _ -> printf "return currentexpr\n"; currentexpr
                                            | _, Empty -> printf "reuturn literal\n"; Expr(Literal(cur))
                                            | _,_ -> printf "return concat\n"; Expr(Concat(currentexpr, parseE(tokens, &next)))
                                *)
                                next <- next + 1

                                
                                match cur with
                                    | "" -> islost<-false; Expr(Empty)
                                    | _ -> debug("parseE: noMatch: return literal: " + cur); Expr(Literal(cur))
            let nx = if (next < tokens.Length) then tokens.[next] else ""

            printf "mval: %s; next: %s; islost:%b; stop:%b; tokenstart: %i; nexttokenindex: %i; tokenlength:%i\n" mval nx islost stop tokenstart next tokens.Length 
            match ignore, islost with
                | true, _ -> printf("parseE: do nothing\n"); ()
                | _, true when currentexpr.Expr <> Empty -> printf("parseE: concat\n"); currentexpr <- Expr(Concat(currentexpr, tmp)); currentexpr.IsContract <- true;
                | _ -> printf("parseE: overwrite; next: %i; tokens.length: %i\n") next tokens.Length; currentexpr <- tmp
            ()

        currentexpr


        //Array.iter(fun el -> debug("current token: " + el.ToString()))tokens
    
            
    let tokenizer(str:string, chars:char[]) : string[] = 
        let mutable result = [| |]
        let mutable currentstring = ""
        for i=0 to str.Length - 1 do
            let cchar = str.Chars(i)
            if Array.exists(fun el -> el.Equals(cchar))chars then  
                result <- if currentstring.Length > 0 then Array.append result [| currentstring; cchar.ToString() |] 
                            else Array.append result [| cchar.ToString() |]
                currentstring <- ""
            else currentstring <- currentstring + cchar.ToString()
        if currentstring <> "" then result <- Array.append result [| currentstring |]
        result



    let parse2(_str:string) : Expr =
        let mutable next = 0
        let str = _str
        let tokens = tokenizer(str, [| '+'; '-'; '*'; '/'; '('; ')'; ','; '='; '['; ']'; '\\'; '^'; '_'; '{'; '}'; ' '; '>'; '<'; |])  
        let mutable currentexpr = parseE(tokens, &next)
        currentexpr
